package metagrammar;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import metagrammar.Operator.Instance;
import metagrammar.Transform.Variable;
import metagrammar.Transform.Wildcard;

import org.junit.Before;
import org.junit.Test;

import com.google.common.collect.ImmutableList;

public class TransformSpecTest {
    
    private Operator add;
    private Operator multiply;
    private Operator number;
    
    @Before
    public void setUp() throws Exception
    {
        Type number = new Type("number");
        Type[] numberPair = { number, number };
        add = new Operator(number, numberPair);
        multiply = new Operator(number, numberPair);
        this.number = new Operator(number);
    }
    
    @Test
    public void testTransformSpec()
    {
        assertNotNull(new Transform(add.getInstance(), multiply.getInstance()));
    }
    
    @Test
    public void testSimplestIsValid()
    {
        metagrammar.Operator.Instance addi = add.getInstance();
        addi.set(0, number.getInstance());
        addi.set(1, number.getInstance());
        Transform transform = new Transform(addi, addi);
        assertTrue(transform.isValid(addi, addi));
        assertFalse(transform.isValid(addi, multiply.getInstance()));
    }
    
    @Test
    public void testSimplestValidWithVariables()
    {
        Variable var1 = new Variable(number);
        Variable var2 = new Variable(number);
        Operator.Instance add1 = add.getInstance();
        add1.set(0, var1);
        add1.set(1, var2);
        metagrammar.Operator.Instance add2 = add.getInstance();
        add2.set(0, var2);
        add2.set(1, var1);
        Transform transform = new Transform(add1, add2);
        metagrammar.Operator.Instance number1 = number.getInstance();
        metagrammar.Operator.Instance number2 = number.getInstance();
        add1 = add.getInstance();
        add1.set(0, number1);
        add1.set(1, number2);
        add2 = add.getInstance();
        add2.set(0, number2);
        add2.set(1, number1);
        assertTrue(transform.isValid(add1, add2));
    }
    
    @Test
    public void testIsValidInput()
    {
        metagrammar.Operator.Instance addI = add.getInstance();
        addI.add(0, new Variable(number));
        addI.add(1, new Wildcard(number));
        Transform transform = new Transform(addI, addI);
        assertTrue(transform.isValidInput(addI));
        assertFalse(transform.isValidInput(multiply.getInstance()));
        // TODO
    }
    
    @Test
    public void testTransform()
    {
        Variable a = new Variable(number);
        Variable b = new Variable(number);
        Variable c = new Variable(number);
        metagrammar.Operator.Instance addLeft = add.getInstance();
        addLeft.set(0, a);
        addLeft.set(1, b);
        Operator.Instance multleft = multiply.getInstance();
        multleft.set(0, addLeft);
        multleft.set(1, c);
        metagrammar.Operator.Instance mult1 = multiply.getInstance();
        mult1.set(0, a);
        mult1.set(1, c);
        metagrammar.Operator.Instance mult2 = multiply.getInstance();
        mult2.set(0, b);
        mult2.set(1, c);
        metagrammar.Operator.Instance addRight = add.getInstance();
        addRight.set(0, mult1);
        addRight.set(1, mult2);
        Transform transform = new Transform(multleft, addRight);
        metagrammar.Operator.Instance addinput = add.getInstance();
        metagrammar.Operator.Instance A = number.getInstance();
        addinput.set(0, A);
        metagrammar.Operator.Instance B = number.getInstance();
        addinput.set(1, B);
        Instance rootinput = multiply.getInstance();
        rootinput.set(0, addinput);
        metagrammar.Operator.Instance C = number.getInstance();
        rootinput.set(1, C);
        metagrammar.Operator.Instance output = transform.transform(rootinput);
        assertTrue(output.getOperator().equals(add));
        ImmutableList<metagrammar.Operator.Instance> tuple = output.getInputTuple();
        assertEquals(2, tuple.size());
        metagrammar.Operator.Instance left = tuple.get(0);
        assertEquals(multiply, left.getOperator());
        ImmutableList<metagrammar.Operator.Instance> leftTuple = left.getInputTuple();
        assertEquals(2, leftTuple.size());
        assertEquals(A, leftTuple.get(0));
        assertEquals(C, leftTuple.get(1));
        metagrammar.Operator.Instance right = tuple.get(1);
        assertEquals(multiply, right.getOperator());
        ImmutableList<metagrammar.Operator.Instance> rightTuple = right.getInputTuple();
        assertEquals(2, rightTuple.size());
        assertEquals(B, rightTuple.get(0));
        assertEquals(C, rightTuple.get(1));
    }
    
}
